Draft 5/24/2002
The
PowerKernel Architecture
The PowerKernel is a cluster-enabled middleware framework
for programming Java back-end modules for a variety of front-ends provided by
the PowerKernel to service multiple types of clients. Using the same simple code model on the
back-end, you can access the modules as dynamic content, and this dynamic
content may utilize XSL rendering and other built-in functionality. PowerKernel modules can be accessed as Web
Services, or pure Java clients utilizing object serialization as the
transport. The modules methods can be
overloaded to access different related services differing only by their method
parameters supplied by the client. Support
for session binding and parallel computing through clustered PowerKernels is
inherent in the code model. Load-on-demand handlers that service user code can
be built on the same model the system itself uses for much of its
functionality.
In a simple declarative fashion, the model supports
applications that may need asynchronous communication.
The system also supports a Tuple construct called the Packet
class that can function in manner similar to other Tuple Space parallel
computing systems such as Sun JavaSpaces/Jini, IBM TSpaces, and the original
David Gelernters Linda.
Accessing
the PowerKernel
We examine the use of pure a Java client accessing back-end
services first.
import com.neocoretechs.powerspaces.*;
import java.io.*;
import java.util.*;
public class ping
{
/**
* Connect to kernel running on defualt
port and invoke ping
* @param argv[0]
The URI to connect to
*/
public static
void main(String[] argv) throws Exception {
// provide java client
with URL
PowerSpace PS = new PowerSpace(argv[0]);
PKRemote pkr = PS.getRemote("com.neocoretechs.powerspaces.server.handler.PSIPCHandler");
//
for(int
i= 0 ; i < 100000 ; i++) {
long st = System.currentTimeMillis();
System.out.println("Start time
"+String.valueOf(st));
Object o = pkr.invoke("Ping");
System.out.println("Synch time
"+String.valueOf(System.currentTimeMillis()-st)+" ms. Count: " + i);
System.out.println(o);
}
PS.Unplug();
//
}
}
The first step is to obtain the class using the getRemote
method. This causes the class to be
loaded and a remote reference to be created in the client. PowerKernel back-end methods are invoked
static, so the reference essentially contains connection information. Using the invoke method of the remote
reference we can invoke methods on the back-end.
package
com.neocoretechs.powerspaces.server.handler;
import com.neocoretechs.powerspaces.*;
import com.neocoretechs.powerspaces.server.*;
import java.util.*;
import java.io.*;
public class PSIPCHandler {
public static String PowerKernel_Ping(
Integer leg, CustomerConnectionPanel ccp) throws PowerSpaceException
{
Long tim = new Long(System.currentTimeMillis());
return tim.toString();
}
}
If we wanted to pass arguments to the method we could write
it as follows:
public static String PowerKernel_Ping(
Integer leg, CustomerConnectionPanel ccp, String
foo, Integer bar) throws PowerSpaceException
{
Long tim = new Long(System.currentTimeMillis());
return
tim.toString()+foo+String.valueOf(bar);
}
And the client invocation line would look like this:
Object o = pkr.invoke("Ping", "yo", new Integer(1));
The command line invocation for the above client module
might appear as follows:
java ping
http://127.0.0.1:8080/nct/servlet/com.neocoretechs.power
spaces.server.PowerSpaceServlet
The PowerKernel reflects the methods in your class that have
"PowerKernel_" prefixing their names and builds tables of the reflected
methods, in a manner similar to the 'Beans' model. This happens when the class is first
referenced. When the method is invoked
the Leg and CustomerConnectionPanel arguments are filled in by the system. The leg represents the connected cluster port
the command originated from and the CustomerConnectionPanel contains session
information and well as a properties table that the system and developer may
use to bind properties to the session.
The CustomerConnectionPanel also allows asynchronous
communication with clients and the cluster by providing methods to queue
messages to various internal queues. The
getSession()
method can be used to obtain the signature of the current session,
typically the Id assigned by the web server.
You would replace your package and class names for com.neocoretechs.powerspaces.server.handler
in the above examples, in your projects.
The PowerKernel can also run standalone using the client
above. When started from the java
command, it can be connected using persistent sockets. In both the Servlet and the standalone
configuration, Java object serialization is used for transport. This is transparent to the client except for
the connection string:
PowerSpace PS
= new PowerSpace("hostname", portnum); // for tcp/ip socket connect
You can also make a back-channel socket connect to the
PowerKernel running in a web container like the Servlet engine.
Exception
Handling
All remote exceptions are propagated back to the client if
not explicitly caught on the server side.
This simplifies remote development and provides a more robust platform.
If we attempted to invoke our ping method with the wrong
numner of parameters, the server would respond with an exception manifesting
itself as follows:
Remote
Exception:
Exception in
thread "main" java.lang.NoSuchMethodException: Method Ping not foun
d in
com.neocoretechs.powerspaces.server.handler.PSIPCHandler Wrong number of pa
rameters
Support
for Asynchronous Execution
If we wish to delay return from a method call until some
other processing occurs we can declare a void return type, which will cause the
client to wait until a result is queued for return to the client.
Using the queuePacket method of CustomerConnectionPanel
static CustomerConnectionPanel waiterCCP;
public static void PowerKernel_PingWait( Integer leg, CustomerConnectionPanel
ccp) throws PowerSpaceException, IOException, ClassNotFoundException
{
waiterCCP = ccp;
}
public static String PowerKernel_PingNotify( Integer leg, CustomerConnectionPanel
ccp) throws PowerSpaceException, IOException, ClassNotFoundException
{
Long tim = new Long(System.currentTimeMillis());
waiterCCP.queuePacket(tim.toString());
return "Ok";
}
In the client application, two threads containing client
connections and remote references would, perhaps not in the same Virtual
Machines or on the same physical machines, invoke the above methods. PingWait would wait until PingNotify had been
called and queued the payload with the system time
back to the other client.
Since this system is primarily request/response based, as is
most dynamic content especially regarding the expectations of browser clients,
the void
return type was chosen as the most semantically relevant construct. The model does require return types where one
may not desire to do so, and in that case returning a simple string such as
"Ok" was deemed adequate to fulfill the request/response contract.
Dynamic
Content Generation
Using the testContent method in our PSIPCHandler class, we
can type the URI for this method into the browser and return the results as
dynamic content. The arguments encoded
in the query string of the URL are transformed into a Hashtable by the
ContentServlet. The ContentServlet passes
it to the class and the method encoded in the request. The Hashtable passed to the testContent
method is turned in to a string of key=value items. The method returns a type
of TextContentType containing the created string. A client requesting services through the
com.neocoretechs.powerspaces.server.ContentServlet
or the
com.neocoretechs.powerspaces.server.TransientContentServlet
is expected to
return a type
derived
from ContentTypeInterface. The
TransientContentServlet is a version of ContentServlet that does not create a
session binding or send a cookie. This
is used for higher-performance apps that don't require a session to operate.
The URI:
Produces the resulting:
this=youre
the man
that=dawg
On the server side the testContent method appears as follows:
public static ContentTypeInterface
PowerKernel_testContent(Integer leg, CustomerConnectionPanel ccp, Hashtable
args) throws Exception {
Enumeration e1 = args.keys();
StringBuffer sb = new StringBuffer();
// iterate thru elements in
hashtable, extracting key/value
// pairs...return as text
content to browser
for(int
i = 0; i < args.size(); i++) {
String elem = (String)e1.nextElement();
sb.append(elem);
sb.append("=");
// elems come back as
string array...there may be
// multiple elements per name
depending on your
// request format...
String[]
elems = (String[])args.get(elem);
for(int
j = 0 ; j < elems.length; j++)
sb.append(elems[j]+"
");
sb.append("\r\n");
}
return
new TextContentType(sb.toString());
}
In addition to the TextContentType, the GifContentType is
available for returning dynamic images.
In the above example, we could replace
return new TextContentType(sb.toString());
With
return new
GifContentType(toImageByteArray(sb.toString()));
Assuming we had a method called toImageByteArray that took a
string and converted it to an array of GIF image bytes with the string text.
The HTMLContentType is located in located in the
com.neocoretechs.xslt package and is provides not only a straight HTML string
as a constructor argument, but also has overloaded constructor methods for
supplying the XSL style sheet file or Reader, XML input string, and other
optional overloads.
/**
*Constructor
for plain ole HMTL
*/
public
HTMLContentType(String htmlText) {
content =
htmlText;
}
public
HTMLContentType(Reader xmlInput, Reader stylesheet) throws Exception {
content =
processTransformation(new InputSource(xmlInput), new InputSource(stylesheet));
}
public
HTMLContentType(File xmlInput, File stylesheet) throws Exception {
content =
processTransformation(fileInputSource(xmlInput), fileInputSource(stylesheet));
}
public
HTMLContentType(File xmlInput, File stylesheetOne, File stylesheetTwo)
throws
Exception {
String result = processTransformation(fileInputSource(xmlInput),
fileInputSource(stylesheetOne));
InputSource firstResultStream = new InputSource(new StringReader(result));
content =
processTransformation(firstResultStream, fileInputSource(stylesheetTwo));
}
public
HTMLContentType(String txml, File stylesheet) throws Exception {
content =
processTransformation(new InputSource(new
ByteArrayInputStream(txml.getBytes())), fileInputSource(stylesheet));
}
Getting Data to The Model
Getting data into the model in web-based applications has
spawned quite a few diverse technologies.
Fundamentally, HTTP has two ways to do this: GET and POST. The PowerKernel supports both GET and POST
transparently, producing a cohesive method call of the same type regardless of
HTTP method. The above URI's in the
examples could have been formatted using a HTML FORM tag and POST and produced
the same result. We saw an example of a
Hashtable being formed from arbitrarily named parameters in the query string of
the URL that produced "youre the man, dawg" in the above
example. If we had formatted it using
the reserved names param0, param1, param2… then the request would have been
passed to a method with multiple string arguments rather than a Hashtable. So the URI:
http://127.0.0.1:8080/nct/servlet/com.neocoretechs.powerspaces.server.ContentServlet?class=com.neocoretechs.powerspaces.server.handler.PSIPCHandler&method=testContent¶m0=youre%20the%20man¶m1=dawg
Would attempt to invoke the method:
public static ContentTypeInterface
PowerKernel_testContent(Integer leg, CustomerConnectionPanel ccp, String you, String man) throws
Exception { }
A series of parameters with the designation param0 will
create an array of Objects with the values and pass those to the method:
http://127.0.0.1:8080/nct/servlet/com.neocoretechs.powerspaces.server.ContentServlet?class=com.neocoretechs.powerspaces.server.handler.PSIPCHandler&method=testContent¶m0=youre%20the%20man¶m0=dawg
Would attempt to invoke the method:
public static ContentTypeInterface
PowerKernel_testContent(Integer leg, CustomerConnectionPanel ccp, Object[] mananddawg) throws Exception {
}
and
the call would contain an array of the 2 elements
Forms
Submission for Dynamic Content Method Call
Many times the GET method is insufficient for complex
applications. Where there are many
fields or simply a forms-based app, or perhaps where the argument length
exceeds the limit for GET, a forms POST must be employed. We will attempt to combine several concepts
and demonstrate a forms POST generated from an XSL style sheet with JavaScript
methods to allow us to dynamically set the form properties for class and method
at runtime. By employing these methods,
a generic forms-based submission system is constructed. The JavaScript to do the actual POST is as
follows:
<script
language="JavaScript1.2">
<!--
var server =
'../../servlet/com.neocoretechs.powerspaces.server.ContentServlet';
function doSubmit(tclass, tmethod, tform) {
tform.action = server;
tform.elements['class'].value
= tclass;
tform.elements['method'].value
= tmethod;
alert("Submitting
"+tform.action+" "+tform.elements['class'].value+"
"+tform.elements['method'].value);
tform.submit();
}
Where the arguments are class name, method name, and FORM
object to POST.
And an example of calling this function
from another JavaScript function:
function submitRec(form1) {
parent.gatewayFrame.doSubmit('com.neocoretechs.imagepump.VectorMapHandler','updateCurrent',
form1);
}
And the function to locate the form via the index in the XML
structure in order to submit one of many forms on the page:
function submitRec(index) {
window.opener.submitRec(document.forms['form'+index]);
}
The XSL style sheet containing the content to transform will
be working on this XML structure:
<layer
name="Underwater">
<schema>
<field
type="String" size="40" scale="0" display="A
corp">Company</field>
<field
type="String" size="20" scale="0"
display="Stock thingie">Symbol</field>
<field type="String"
size="45" scale="0" display="Where
crashing">Exchange</field>
</schema>
<record index="0">
<field
index="1">XML Innovations</field>
<field
index="2">XMLI</field>
<field
index="3">Nasdaq NMS</field>
</record>
<record index="1">
<field index="1">My 2by4</field>
<field
index="2">BYBY</field>
<field
index="3">Nasdaq NMS</field>
</record>
<record index="2">
<field
index="1">FlatStone</field>
<field
index="2">FKED</field>
<field
index="3">Nasdaq NMS</field>
</record>
</layer>
And the relevant section in the XSL
sheet appears as follows:
<TABLE
cols="4">
<xsl:for-each
select="layer/record">
<form>
<xsl:attribute
name="name">form<xsl:value-of
select="@index"/></xsl:attribute>
<xsl:attribute
name="action">#</xsl:attribute>
<input
name="class" type="hidden"
value="#"></input>
<input name="method"
type="hidden" value="#"></input>
<input>
<xsl:attribute
name="name">param0</xsl:attribute>
<xsl:attribute
name="type">hidden</xsl:attribute>
<xsl:attribute
name="value"><xsl:value-of
select="//layer/@name"/></xsl:attribute>
</input>
<input>
<xsl:attribute
name="name">param1</xsl:attribute>
<xsl:attribute
name="type">hidden</xsl:attribute>
<xsl:attribute
name="value"><xsl:value-of
select="@index"/></xsl:attribute>
</input>
<xsl:for-each
select="field[@index]">
<TR
bgColor="#ffffff">
<TD
width="150">
<xsl:variable name="index"><xsl:value-of
select="@index"/></xsl:variable>
<xsl:value-of
select="/layer/schema/field[number($index)]/@display"/>
</TD>
<TD width="500">
<FONT size="-1"></FONT>
<input>
<xsl:attribute
name="type">text</xsl:attribute>
<xsl:attribute
name="name">param2</xsl:attribute>
<xsl:variable name="index"><xsl:value-of
select="@index"/></xsl:variable>
<xsl:attribute
name="size"><xsl:value-of
select="/layer/schema/field[number($index)]/@size"/></xsl:attribute>
<xsl:attribute
name="value"><xsl:value-of
select="current()"/></xsl:attribute>
</input>
</TD>
</TR>
</xsl:for-each>
</form>
<TR
bgColor="#ffffff">
<TD width="500">
<FONT
size="-1"></FONT>
<form
name="submitform" >
<input>
<xsl:attribute
name="type">button</xsl:attribute>
<xsl:attribute
name="name">Submit1</xsl:attribute>
<xsl:attribute
name="value">Submit</xsl:attribute>
<xsl:attribute name="onClick"
>submitRec(<xsl:value-of
select="@index"/>)</xsl:attribute>
</input>
</form>
</TD>
</TR>
</xsl:for-each>
</TABLE>
What we in effect have above is a generic form that contains
as many text boxes as there are records in the XML. Each can be edited and submitted to the
PowerKernel methods with a submit button.
If we rendered the sheet against the XML we would produce the following:
And the rendered portion would appear as follows in the
right hand pane:
Looking at the XSL, we see that the name of the form is set
to the index, the class and method are filled in dynamically by the JavaScript
functions, and the first two parameters to the PowerKernel method are the
'layer name' and index, placed in param0 and param1. For each text box that appears, the name
param2 is assigned, which as we discussed earlier creates an array that is
passed to the third argument of the PowerKernel method.
In conclusion, we explored how data gets into the model, how
to manipulate the view, and the ways in which PowerKernel acts as a controller
and handles much of the housekeeping associated with a more declarative
approach such as configuration files, etc.
Rich dynamic content generation systems can be, and have
been, developed using the PowerKernel.
Some examples are: Web-based geographic information system, advertising
delivery engine, pluggable VPN dynamic configuration controller, and real-time
wireless remote GPS tracking system with dynamic mapping. In addition, the content is completely
separate from the business logic. No
code in pages, sans custom tag libraries, and therefore more maintainable.
Accessing
the PowerKernel Web
Services
The back-end modules of the PowerKernel can be accessed as
Web services by pointing at the proper URI.
By sending a request to the SOAPRouterServlet with the class name
encoded as a parameter in the query string, you can receive the WSDL XSD for
use in Web services development environments or clients.
In this example we are using the PowerKernel through Tomcat
under the old Servlet model, where we use explicit paths vs. WAR files. The system can be repackaged by the user for
the new spec.
Here, we are asking the SOAPRouterServlet for the definition
of com.neocoretechs.powerspaces.server.handler.PSIPCHandler.class. There are many methods in this class with the
Ping method highlighted below.
The
output of the WSDL definition in its entirety follows:
<?xml version="1.0"
encoding="UTF-8" ?>
-
<definitions
targetNamespace="http://127.0.0.1:8080/soap/servlet/com.neocoretechs.powerspaces.server.soap.SOAPRouterServlet"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:serviceNS="http://127.0.0.1:8080/soap/servlet/com.neocoretechs.powerspaces.server.soap.SOAPRouterServlet"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/1999/XMLSchema"
xmlns:xsd1="http://www.neocoretechs.com/schema">
-
<types>
-
<schema elementFormDefault="qualified"
targetNamespace="http://www.neocoretechs.com/schema"
xmlns="http://www.w3.org/1999/XMLSchema"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
-
<complexType name="ArrayOfdouble">
-
<complexContent>
-
<restriction base="SOAP-ENC:Array">
<attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="double[]" />
</restriction>
</complexContent>
</complexType>
-
<complexType name="ArrayOfdouble2D">
-
<complexContent>
-
<restriction base="SOAP-ENC:Array">
<attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="double[][]" />
</restriction>
</complexContent>
</complexType>
-
<complexType name="ArrayOffloat">
-
<complexContent>
-
<restriction base="SOAP-ENC:Array">
<attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="float[]" />
</restriction>
</complexContent>
</complexType>
-
<complexType name="ArrayOffloat2D">
-
<complexContent>
-
<restriction base="SOAP-ENC:Array">
<attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="float[][]" />
</restriction>
</complexContent>
</complexType>
-
<complexType name="ArrayOfstring">
-
<complexContent>
-
<restriction base="SOAP-ENC:Array">
<attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="string[]" />
</restriction>
</complexContent>
</complexType>
-
<complexType name="ArrayOfstring2D">
-
<complexContent>
-
<restriction base="SOAP-ENC:Array">
<attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="string[][]" />
</restriction>
</complexContent>
</complexType>
-
<complexType name="ArrayOfint">
-
<complexContent>
-
<restriction base="SOAP-ENC:Array">
<attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="int[]" />
</restriction>
</complexContent>
</complexType>
-
<complexType name="ArrayOfint2D">
-
<complexContent>
-
<restriction base="SOAP-ENC:Array">
<attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="int[][]" />
</restriction>
</complexContent>
</complexType>
-
<complexType name="base64Ofbyte">
-
<all>
<element name="element"
type="xsd:base64Binary" />
</all>
</complexType>
</schema>
</types>
-
<message name="ConnectRequest">
<part name="Connect_param0"
type="xsd:int" />
<part name="Connect_param1"
type="xsd:int" />
<part name="Connect_param2"
type="xsd:string" />
<part name="Connect_param3"
type="xsd:string" />
</message>
-
<message name="PingResponse">
<part name="return"
type="xsd:string" />
</message>
-
<message name="ConnectNewPowerPlantResponse">
<part name="return"
type="xsd:ur-type" />
</message>
-
<message name="InstallHandlerResponse">
<part name="return"
type="xsd:ur-type" />
</message>
-
<message name="testContentRequest">
<part name="testContent_param0"
type="xsd:Map" />
</message>
-
<message name="PingRequest">
<part name="Ping_param0"
type="xsd:string" />
</message>
<message name="CollectRequest"
/>
-
<message name="ConnectResponse">
<part name="return"
type="xsd:ur-type" />
</message>
-
<message name="ConnectNewPowerPlantRequest">
<part
name="ConnectNewPowerPlant_param0" type="xsd:int"
/>
<part
name="ConnectNewPowerPlant_param1" type="xsd:string"
/>
<part
name="ConnectNewPowerPlant_param2" type="xsd:string"
/>
</message>
-
<message name="InstallHandlerRequest">
<part
name="InstallHandler_param0" type="xsd:string"
/>
<part
name="InstallHandler_param1" type="xsd1:base64Ofbyte" />
</message>
-
<portType name="PSIPCHandlerPortType">
-
<operation name="Connect">
<input message="serviceNS:ConnectRequest" />
<output message="serviceNS:ConnectResponse" />
</operation>
- <operation name="Ping">
<input message="serviceNS:PingRequest"
/>
<output message="serviceNS:PingResponse"
/>
</operation>
-
<operation name="ConnectNewPowerPlant">
<input message="serviceNS:ConnectNewPowerPlantRequest" />
<output message="serviceNS:ConnectNewPowerPlantResponse" />
</operation>
-
<operation name="InstallHandler">
<input message="serviceNS:InstallHandlerRequest" />
<output message="serviceNS:InstallHandlerResponse" />
</operation>
-
<operation name="InstallHandler">
<input message="serviceNS:InstallHandlerRequest" />
<output message="serviceNS:InstallHandlerResponse" />
</operation>
</portType>
-
<binding name="PSIPCHandlerSoapBinding" type="serviceNS:PSIPCHandlerPortType">
<soap:binding
style="rpc"
transport="http://schemas.xmlsoap.org/soap/http" />
-
<operation name="Connect">
<soap:operation
soapAction="" style="rpc" />
-
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler"
use="encoded" />
</input>
-
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler"
use="encoded" />
</output>
</operation>
-
<operation name="Ping">
<soap:operation
soapAction="" style="rpc" />
-
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler"
use="encoded" />
</input>
-
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler"
use="encoded" />
</output>
</operation>
-
<operation name="ConnectNewPowerPlant">
<soap:operation
soapAction="" style="rpc" />
-
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler"
use="encoded" />
</input>
-
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler"
use="encoded" />
</output>
</operation>
-
<operation name="InstallHandler">
<soap:operation
soapAction="" style="rpc" />
-
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler"
use="encoded" />
</input>
-
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler"
use="encoded" />
</output>
</operation>
-
<operation name="InstallHandler">
<soap:operation
soapAction="" style="rpc" />
-
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler"
use="encoded" />
</input>
-
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="com.neocoretechs.powerspaces.server.handler.PSIPCHandler"
use="encoded" />
</output>
</operation>
</binding>
-
<service name="PSIPCHandler">
-
<port binding="serviceNS:PSIPCHandlerSoapBinding"
name="PSIPCHandlerPort">
<soap:address
location="http://127.0.0.1:8080/soap/servlet/com.neocoretechs.powerspaces.server.soap.SOAPRouterServlet"
/>
</port>
</service>
</definitions>
Below is a screen shot of the web service schema for the
class in the above example from a browser client which displays formatted
schema WSDL XSD XML:
A current limitation is that Web services do not support
overloading of methods. The Web services
model does not support it so you must create additional PowerKernel method
wrappers to disambiguate them to Web services.
Dr. David Gelernter at Yale
The
Packet Class as Tuple
The Tuple Space or Linda model of computing was developed as
a parallel computing architecture. A
great contributor to the field, Gelernter, devised a simple protocol for
complex data structures composed of arbitrary fields of 'tuples' to operate
within a 'space'; or shared communications buffer. The fields in the tuples can be of formal or
value (aka actual) type. The formal type
is analogous to the Java Class class, or class definition. Formal types are used in the space to denote
a template that may match others of formal type or instances of that formal
type. Actual types are instances of
classes. The operations on the space are
simplicity themselves; in, out, and waitFor.
Others functions such as read without taking out are supported to
different degrees in variations of the model.
'in' puts a tuple in the
space. 'out'
removes all instances of a tuple matching the supplied tuple and returns them
to the caller. 'waitFor'
is used to block until a tuple matching the target enters the space, at which
time it is removed and sent to the caller.
Using these simple constructs, a world of parallel computing
can be realized. The Packet class in
PowerKernel is analogous to the tuple in other systems. The Packet constructor can be called with an
arbitrary argument list of objects to be packaged in the tuple.
import com.neocoretechs.powerspaces.*;
import java.io.*;
import java.util.*;
public class space1
{
/**
* Connect to kernel running and invoke
space
* @param argv[0]
The host to connect to
*/
public static
void main(String[] argv) throws Exception {
PowerSpace PS = new PowerSpace(argv[0]);
PKRemote pkr = PS.getRemote("com.neocoretechs.powerspaces.server.handler.PSSpaceHandler");
System.out.println(pkr.invoke("in",new
Packet("this","is","a",new Integer(1),new
Integer(2),new Integer(3))));
System.out.println(pkr.invoke("in",new
Packet("this","is","a",new Integer(1),new
Integer(2),new Integer(4))));
System.out.println(pkr.invoke("in",new
Packet("this","is","a",new Integer(1),new
Integer(2),new Integer(5))));
System.out.println(pkr.invoke("in",new
Packet("this","is","a",new Integer(1),new
Integer(2),new Integer(6))));
System.out.println(pkr.invoke("in",new
Packet("this","is","a",new Integer(1),new Integer(2),new
Long(7))));
PS.Unplug();
}
}
Notice that we are placing 5 similar tuples in the space,
but the last one has an argument of type Long.
We now construct a module to retrieve only the Packets matching those
with the last argument of type Integer:
import com.neocoretechs.powerspaces.*;
import java.io.*;
import java.util.*;
public class space2
{
/**
* Connect to kernel running and invoke
space
* @param argv[0]
The host to connect to
*/
public static void main(String[] argv) throws Exception {
PowerSpace PS = new PowerSpace(argv[0]);
PKRemote pkr = PS.getRemote("com.neocoretechs.powerspaces.server.handler.PSSpaceHandler");
// construct a template to get
Tuples
System.out.println(pkr.invoke("out",new
Packet("this","is","a",new Integer(1),new
Integer(2),new Field(Integer.class))));
PS.Unplug();
}
}
The result being returned is a Packet of Packets retrieved
from the space:
[ [ "this", "is",
"a", 1, 2, 3 ], [ "this", "is", "a", 1,
2, 4 ], [ "this", "is"
,
"a", 1, 2, 5 ], [ "this",
"is", "a", 1, 2, 6 ] ]
In the format displayed by the Packet class containing other
Packets. Notice the absence of '7'. Since it was of formal type Long the template
did not match it.
import com.neocoretechs.powerspaces.*;
import java.io.*;
import java.util.*;
public class space3
{
/**
* Connect to kernel and invoke space
* @param argv[0]
The URI to connect to
*/
public static
void main(String[] argv) throws Exception {
PowerSpace PS = new PowerSpace(argv[0]);
PKRemote pkr = PS.getRemote("com.neocoretechs.powerspaces.server.handler.PSSpaceHandler");
// construct a template
to get Tuples
System.out.println(pkr.invoke("waitFor",new
Packet("this","is","a",new Integer(1),new
Integer(2),new Field(Long.class))));
PS.Unplug();
}
}
This method will block until the matching tuple
appears. If we run space1 again from
another client at the same time, the last tuple inserted will, match and this
client will return.
Cluster
Architecture
The cluster is designed to be connected in a geodesic web as
illustrated below. Up to 9223 trillion
nodes may be connected.
The nodes are connected using persistent TCP/IP socket
connections. On for
inbound traffic and another for outbound on each 'leg' of the node. The legs are designated as Left, Right,
Parent, Left Crossbar and Right Crossbar.
A parent must connect a child node into the cluster from its left or
right legs to the child's parent ports.
A crossbar connection is made from the right node/left crossbar legs to
left node/right crossbar. Edge nodes
are joined the same way. Here, this form of quorum adds a measure of
security. It is not possible to connect
into the cluster without action by the parent,
however, this action can be performed programatically, so the cluster may be
able to maliciously connect if compromised.
Traffic through the cluster is passed from node to node using the Packet
class described above. Transport through
the cluster using the Packet class is necessary due to the need to add routing
and messaging elements to the Packet dynamically. In addition, the results of
a parallel operation involving an arbitrary number of nodes requires a
Packet to represent the unreduced results if that is the goal of the application. Subtypes of Packet are used for routing and
point-to-point messaging and a Packet may transported through the cluster
contains a command payload which may be broadcast for parallel operations or a
point-to-point message.
Synchronizing and reduction queues on each node provide
barrier synchronization.
Cluster |
A configuration file called PowerSpaces.ini in the directory
from which the PowerPlant was started can be used to set the port ranges. The ranges are from the main port to main
port+11, since multiple sockets are used for
interconnect. The format of the file is
as follows:
MainPort:8202
ParentInBind:127.0.0.1
ParentOutBind:127.0.0.1
LeftInBind:127.0.0.1
LeftOutBind:127.0.0.1
RightInBind:127.0.0.1
RightOutBind:127.0.0.1
LeftCrossbarInBind:127.0.0.1
LeftCrossbarOutBind:127.0.0.1
RightCrossbarInBind:127.0.0.1
RightCrossbarOutBind:127.0.0.1
Each input and output port on each leg may be bound to
specific network interfaces for increased throughput.
The following module may be used to connect clustered
PowerKernels:
import com.neocoretechs.powerspaces.*;
import java.io.*;
import java.util.*;
public class connect
{
public static
void main(String[] argv) {
try {
PowerSpace PS = new PowerSpace(argv[0], 8202);
PKRemote pkr = PS.getRemote("com.neocoretechs.powerspaces.server.handler.PSIPCHandler");
PKRemote pkp = PS.getRemote("com.neocoretechs.powerspaces.server.handler.PKParallel");
long
st = System.currentTimeMillis();
System.out.println("Start
time "+String.valueOf(st));
Packet ppp = (Packet)(pkp.invoke("BroadcastAll",
new
Packet("com.neocoretechs.powerspaces.server.handler.PSIPCHandler",
"FindConnectPoint", new Packet())) );
System.out.println(ppp);
Packet pp = (Packet)(ppp.getField(0).value());
Long tclusterID = (Long)(pp.getField(0).value());
ppp
= (Packet)(pkp.invoke("SendMessage",
new Packet(tclusterID,
"com.neocoretechs.powerspaces.server.handler.PSIPCHandler","ConnectNewPowerPlant",
new Packet(new Integer(8302),argv[1], argv[2]))) );
System.out.println("Cluster synch time
"+String.valueOf(System.currentTimeMillis()-st)+" ms");
System.out.println(ppp);
} catch(Exception
e) { System.out.println(e); e.printStackTrace(); }
}
}
The arguments to the above module are the PowerKernel root
node, the host name of the node that is being connected to for the input and
the host for the output (typically the same, though multiple network interfaces
can be used on one machine for higher throughput). So for two PowerKernels running on the same
node with base ports 8202 and 8203 as above, the command line would be: java
connect localhost localhost localhost. The first
invocation broadcasts the method call FindConnectPoint using the BroadcastAll
method of com.neocoretechs.powerspaces.server.handler.PKParallel. BroadcastAll takes a Packet of class, method,
and return Packet. The return packet can
be manipulated by each node and returned to the caller after its journey
through the cluster. FindConnectPoint
begins execution on each node in the cluster.
Each node determines the number of unused legs it possesses, and returns
that number and its id to the parent node.
The parent node determines the most eligible leg and returns that to its
parent. The reduction is performed at
each node until the root, where the most eligible has been reduced by the nodes
below. This target is sent a message to
connect the new node in the second invocation: ConnectNewPowerPlant, whose
first argument is the target cluster Id of the most eligible cluster node
reduction. The SendMessage method takes a cluster Id, a class
name and a method, and a Packet payload containing the arguments for that
method.
Other methods of the PKParallel class permit balanced
processing. The Balance method takes a
command payload packet and distributes it to the next available node for
execution based on a spinner located at each node. A Collect method is used to gather results
from the reduction queues. The methods
themselves, by using the CustomerConnectionPanel and ConnectionPanel, and the
Leg argument obtain the necessary interaction to perform a wide range of
parallel processing. By mastering the
above concepts and applying the same methods using the connection example,
application scalability can be achieved in a manner beyond the usual
high-availability and load balancing schemes.